package com.nova.bluetooth.rtasrWSS;

import android.content.Context;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.nova.bluetooth.helpUtils.LogUtil;


import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.handshake.ServerHandshake;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * 实时转写调用
 * 接口地址
 * https://www.xfyun.cn/services/rtasr?target=price
 * @author white
 */
public class XunFeiRTASRManager {
    // appid 请到讯飞平台申请
    private static final String APPID = "6ee9b206";
    // appid对应的secret_key  请到讯飞平台申请
    private static final String SECRET_KEY = "61acea8809d3224b91ba4622852159ed";



    //    private static final String APPID = "d5335e97";
    //    private static final String SECRET_KEY = "efa08bcde2145125e7b9cccc9871f92e";
    private static volatile XunFeiRTASRManager instance;
    private static final Object lock = new Object();
    private Context mContext;

    public void setContext(Context mContext) {
        this.mContext = mContext;
    }

    private XunFeiRTASRManager() {
        // 私有化构造方法
    }

    public static XunFeiRTASRManager getInstance() {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new XunFeiRTASRManager();
                }
            }
        }
        return instance;
    }

    private static final String TAG = XunFeiRTASRManager.class.getSimpleName() + ": ";

    // 请求地址
    private static final String HOST = "rtasr.xfyun.cn/v1/ws";

    private static final String BASE_URL = "wss://" + HOST;

    private static final String ORIGIN = "https://" + HOST;

    // 音频文件路径
    private static final String AUDIO_PATH = "./resource/test_1.pcm";
    //    private static String filePath = "audio/test_1.pcm"; //测试音频
    private static String filePath = "audio/标准录音1.wav"; //测试音频

    //标准录音1.wav
    // 每次发送的数据大小 1280 字节
    private static final int CHUNCKED_SIZE = 1280;

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS");


    private static boolean stopWebSocketClientTag = false;
    private static MyWebSocketClient client;

    private static final ExecutorService executorService = Executors.newCachedThreadPool();

    public void startWebSocketClient() {
        executorService.submit(this::initWebSocketClient);
    }
    public void startWebSocketClientWithFile(String filePath) {
        executorService.submit( () -> {
            initWebSocketClientWithFile(filePath);
        });
    }

    public void stopRTASRManager() {
        stopWebSocketClientTag = true;
        // 发送结束标识
        send(client, "{\"end\": true}".getBytes());
        LogUtil.i(TAG + getCurrentTimeStr() + "\t发送结束标识完成");
    }

    public static void sendData(byte[] data) {
        if (client != null && client.isOpen()) {
            send(client, data);
        } else {
            LogUtil.e("WebSocketClient 未打开！");
            isWebSocketRunning = false;
        }
    }


    public static volatile boolean isWebSocketRunning = false;

    private void initWebSocketClientWithFile(String filePath) {
        if (client != null && client.isOpen()) {
            return;
        }
        new Thread(() -> {
            try {
                while (true) {
                    stopWebSocketClientTag = false;
                    URI url = new URI(BASE_URL + getHandShakeParams(APPID, SECRET_KEY));
                    DraftWithOrigin draft = new DraftWithOrigin(ORIGIN);
                    CountDownLatch handshakeSuccess = new CountDownLatch(1);
                    CountDownLatch connectClose = new CountDownLatch(1);
                    client = new MyWebSocketClient(url, draft, handshakeSuccess, connectClose);
                    client.connect();

                    while (!client.getReadyState().equals(WebSocket.READYSTATE.OPEN)) {
                        LogUtil.i(TAG + getCurrentTimeStr() + "\t连接中");
                        Thread.sleep(1000);

                        if (stopWebSocketClientTag) {
                            client.close();
                            break;
                        }
                    }

                    // 等待握手成功
                    handshakeSuccess.await();
                    LogUtil.i(TAG + sdf.format(new Date()) + " 发送音频数据准备就绪");
                    isWebSocketRunning = true;

                    writeLocalAudio(client, filePath);

                    // 使用信号量控制结束条件
                    while (!stopWebSocketClientTag) {
                        Thread.sleep(100);  // 适当的休眠，减少CPU空转
                    }

                    // 发送结束标识
                    send(client, "{\"end\": true}".getBytes());
                    LogUtil.i(TAG + getCurrentTimeStr() + "\t发送结束标识完成");

                    // 等待连接关闭
                    connectClose.await();
                    break;
                }
            } catch (URISyntaxException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();
    }


    private void initWebSocketClient() {
        if (client != null && client.isOpen()) {
            return;
        }
        new Thread(() -> {
            try {
                while (true) {
                    stopWebSocketClientTag = false;
                    URI url = new URI(BASE_URL + getHandShakeParams(APPID, SECRET_KEY));
                    DraftWithOrigin draft = new DraftWithOrigin(ORIGIN);
                    CountDownLatch handshakeSuccess = new CountDownLatch(1);
                    CountDownLatch connectClose = new CountDownLatch(1);
                    client = new MyWebSocketClient(url, draft, handshakeSuccess, connectClose);
                    client.connect();

                    while (!client.getReadyState().equals(WebSocket.READYSTATE.OPEN)) {
                        LogUtil.i(TAG + getCurrentTimeStr() + "\t连接中");
                        Thread.sleep(1000);

                        if (stopWebSocketClientTag) {
                            client.close();
                            break;
                        }
                    }

                    // 等待握手成功
                    handshakeSuccess.await();
                    LogUtil.i(TAG + sdf.format(new Date()) + " 发送音频数据准备就绪");
                    isWebSocketRunning = true;

//                    testWriteAssetsAudio(client, mContext);

                    // 使用信号量控制结束条件
                    while (!stopWebSocketClientTag) {
                        Thread.sleep(100);  // 适当的休眠，减少CPU空转
                    }

                    // 发送结束标识
                    send(client, "{\"end\": true}".getBytes());
                    LogUtil.i(TAG + getCurrentTimeStr() + "\t发送结束标识完成");

                    // 等待连接关闭
                    connectClose.await();
                    break;
                }
            } catch (URISyntaxException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();
    }

    private boolean isFirstFrame = true; //判断是不是wav的第一帧，如果是要去掉前面44字节

    private void writeLocalAudio(MyWebSocketClient client, String filePath) {
        byte[] audio = new byte[1280];//1声道1ms有32bytes数据。一次只送部分数据去识别
        // 写入音频
        InputStream in;
        try {
            in = new FileInputStream(filePath);
            isFirstFrame = true;
            int len = -1;
            // 流式读取文件写入
            while ((len = in.read(audio)) != -1) {

                if (stopWebSocketClientTag) break;

                if (filePath.endsWith("wav") && isFirstFrame) {
                    isFirstFrame = false;
                    //去掉wav的44字节文件头
                    byte[] filterWavHeaderData = new byte[audio.length - 44];
                    System.arraycopy(audio, 44, filterWavHeaderData, 0, filterWavHeaderData.length);
                    send(client, filterWavHeaderData);
                } else {
                    //尾部最后一包
                    if (len < CHUNCKED_SIZE) {
                        byte[] endAudio = Arrays.copyOfRange(audio, 0, len);
                        send(client, endAudio);
                        break;
                    }

                    send(client, audio);
                }
                Thread.sleep(10);
            }
            stopWebSocketClientTag = true;
            LogUtil.i(TAG + "音频读取完成");
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 测试Assets文件夹下的文件
     *
     * @param client
     */

    private void testWriteAssetsAudio(MyWebSocketClient client, Context mContext) {
        byte[] audio = new byte[1280];//1声道1ms有32bytes数据。一次只送部分数据去识别
        // 写入音频
        InputStream in;
        try {
            in = mContext.getResources().getAssets().open(filePath);
            isFirstFrame = true;
            int len = -1;
            // 流式读取文件写入
            while ((len = in.read(audio)) != -1) {
                if (filePath.endsWith("wav") && isFirstFrame) {
                    isFirstFrame = false;
                    //去掉wav的44字节文件头
                    byte[] filterWavHeaderData = new byte[audio.length - 44];
                    System.arraycopy(audio, 44, filterWavHeaderData, 0, filterWavHeaderData.length);
                    send(client, filterWavHeaderData);
                } else {
                    //尾部最后一包
                    if (len < CHUNCKED_SIZE) {
                        byte[] endAudio = Arrays.copyOfRange(audio, 0, len);
                        send(client, endAudio);
                        break;
                    }

                    send(client, audio);
                }
                Thread.sleep(10);
            }
            stopWebSocketClientTag = true;
            LogUtil.i(TAG + "音频读取完成");
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private static void testRandomAccessFileSendData(MyWebSocketClient client) {
        // 发送音频
        byte[] bytes = new byte[CHUNCKED_SIZE];
        try (RandomAccessFile raf = new RandomAccessFile(AUDIO_PATH, "r")) {
            int len = -1;
            long lastTs = 0;
            while ((len = raf.read(bytes)) != -1) {
                if (len < CHUNCKED_SIZE) {
                    send(client, bytes = Arrays.copyOfRange(bytes, 0, len));
                    break;
                }

//                long curTs = System.currentTimeMillis();
//                if (lastTs == 0) {
//                    lastTs = System.currentTimeMillis();
//                } else {
//                    long s = curTs - lastTs;
//                    if (s < 40) {
//                        LogUtil.i();("error time interval: " + s + " ms");
//                    }
//                }
                send(client, bytes);
                // 每隔40毫秒发送一次数据
                Thread.sleep(40);
            }
            stopWebSocketClientTag = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static boolean isTranslate = true;
    public static String RTASR_LANG = "cn";
    public static String RTASR_TRANSLATE_LANG = "en";

    // 生成握手参数
    public static String getHandShakeParams(String appId, String secretKey) {
        String ts = System.currentTimeMillis() / 1000 + "";
        String signa = "";
        try {
            signa = EncryptUtil.HmacSHA1Encrypt(EncryptUtil.MD5(appId + ts), secretKey);

            //设置识别的语言与实际不一致，如果是设置是英文，说中文也可以识别出来，实际不应该识别出来
            int mEngLangType = 3;
            if (RTASR_LANG.equals("cn")) {
                mEngLangType = 1;
            }

            String mParams = "";
            if (isTranslate) {
                mParams = "?appid=" + appId + "&ts=" + ts + "&signa=" + URLEncoder.encode(signa, "UTF-8") +
                        "&lang=" + RTASR_LANG +
                        "&transStrategy=2" +
                        "&targetLang=" + RTASR_TRANSLATE_LANG +
                        "&transType=normal" +
                        "&engLangType=" + mEngLangType +
                        "&vadMdn=" + 1 +  //vadMdn  远近场切换，不传此参数或传1代表远场，传2代表近场
                        "&roleType=" + 2;
            } else {
                mParams = "?appid=" + appId + "&ts=" + ts + "&signa=" + URLEncoder.encode(signa, "UTF-8") +
                        "&lang=" + RTASR_LANG +
                        "&transType=normal" +
                        "&engLangType=" + mEngLangType +
                        "&vadMdn=" + 1 +  //vadMdn  远近场切换，不传此参数或传1代表远场，传2代表近场
                        "&roleType=" + 2;  //是否开角色分离，默认不开启，传2开启
            }
            LogUtil.d("test===>mParams:" + mParams);
            return mParams;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }

    public static void send(WebSocketClient client, byte[] bytes) {
        if (client == null || client.isClosed()) {
            LogUtil.e(TAG + "client connect closed!");
            return;
        }
        if (client.isOpen()) {
            client.send(bytes);
        }
    }

    public static String getCurrentTimeStr() {
        return sdf.format(new Date());
    }

    private boolean isTimeOutError = false;

    public class MyWebSocketClient extends WebSocketClient {

        private CountDownLatch handshakeSuccess;
        private CountDownLatch connectClose;

        public MyWebSocketClient(URI serverUri, Draft protocolDraft, CountDownLatch handshakeSuccess, CountDownLatch connectClose) {
            super(serverUri, protocolDraft);
            this.handshakeSuccess = handshakeSuccess;
            this.connectClose = connectClose;
            if (serverUri.toString().contains("wss")) {
                trustAllHosts(this);
            }
        }

        @Override
        public void onOpen(ServerHandshake handshake) {
            LogUtil.i(TAG + getCurrentTimeStr() + "\t连接建立成功！");
        }

        @Override
        public void onMessage(String msg) {
            JSONObject msgObj = JSON.parseObject(msg);
            String action = msgObj.getString("action");
            if (Objects.equals("started", action)) {
                // 握手成功
                LogUtil.i(TAG + getCurrentTimeStr() + "\t握手成功！sid: " + msgObj.getString("sid"));
                handshakeSuccess.countDown();
            } else if (Objects.equals("result", action)) {
                // 转写结果
                String speaking = getContent(msgObj.getString("data"));
//                LogUtil.i(TAG + getCurrentTimeStr() + "\tresult: " + speaking + "==>" +msgObj.getString("data"));
                LogUtil.i("test===>转写识别：" + stringBuffer.toString() + speaking);
//                LogUtil.i("test===>翻译结果：" + translateStringBuffer.toString());


            } else if (Objects.equals("error", action)) {
                String code = msgObj.getString("code");
                String desc = msgObj.getString("desc");
                if (code.equals("10110")) {
                    LogUtil.e("语言未获得授权");
//                    ToastUtils.show("语言未获得授权");
                }

                if (desc.contains("37005")) {
                    LogUtil.e("客户端响应超时");
                    isTimeOutError = true;
                }
                // 连接发生错误
                LogUtil.e(TAG + "Error: " + msg);
            }
        }

        @Override
        public void onError(Exception e) {
            LogUtil.e(TAG + getCurrentTimeStr() + "\t连接发生错误：" + e.getMessage() + ", " + new Date());
            e.printStackTrace();
            isWebSocketRunning = false; // 标记WebSocket线程结束
        }

        @Override
        public void onClose(int arg0, String arg1, boolean arg2) {
            LogUtil.i(TAG + getCurrentTimeStr() + "\t链接关闭");
            connectClose.countDown();
            isWebSocketRunning = false; // 标记WebSocket线程结束
            if (onRTASRResultListener != null){
                onRTASRResultListener.onRTASROffline();
            }

            if (isTimeOutError) {
                //重新建立连接
                startWebSocketClient();
                isTimeOutError = false;
            }
        }

        @Override
        public void onMessage(ByteBuffer bytes) {
            try {
                LogUtil.i(TAG + getCurrentTimeStr() + "\t服务端返回：" + new String(bytes.array(), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        public void trustAllHosts(MyWebSocketClient appClient) {
            LogUtil.i("wss");
            TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[]{};
                }

                @Override
                public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    // TODO Auto-generated method stub

                }

                @Override
                public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    // TODO Auto-generated method stub

                }
            }};

            try {
                SSLContext sc = SSLContext.getInstance("TLS");
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
                appClient.setSocket(sc.getSocketFactory().createSocket());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    public static StringBuffer stringBuffer = new StringBuffer();//从开始到结束的识别结果
    public static StringBuffer translateStringBuffer = new StringBuffer();//从开始到结束的识别翻译结果


    // 把转写结果解析为句子
    public String getContent(String message) {
        LogUtil.d("test====>" + message);
        try {
            JSONObject messageObj = JSON.parseObject(message);
            String biz = messageObj.getString("biz");
            if (biz != null && biz.equals("trans")) {
                String translate = getTranslateData(messageObj);
                if (onRTASRResultListener != null) {
                    onRTASRResultListener.onRTASRTranslateResult(translateStringBuffer.toString() + translate, translateStringBuffer);
                }
            } else {
                int mType = -1;
                StringBuffer resultBuilder = new StringBuffer();

                JSONObject cn = messageObj.getJSONObject("cn");
                JSONObject st = cn.getJSONObject("st");
                String type = st.getString("type");
                mType = Integer.parseInt(type);
                JSONArray rtArr = st.getJSONArray("rt");
                for (int i = 0; i < rtArr.size(); i++) {
                    JSONObject rtArrObj = rtArr.getJSONObject(i);
                    JSONArray wsArr = rtArrObj.getJSONArray("ws");
                    for (int j = 0; j < wsArr.size(); j++) {
                        JSONObject wsArrObj = wsArr.getJSONObject(j);
                        JSONArray cwArr = wsArrObj.getJSONArray("cw");
                        for (int k = 0; k < cwArr.size(); k++) {
                            JSONObject cwArrObj = cwArr.getJSONObject(k);
                            String wStr = cwArrObj.getString("w");
                            resultBuilder.append(wStr);
                        }
                    }
                }

                String str = resultBuilder.toString();
                if (mType == 0) {
                    stringBuffer.append(str);
                    if (onRTASRResultListener != null) {
                        onRTASRResultListener.onRTASRShortResult(str, mType);
                    }
                    str = "";
                } else {

                    if (onRTASRResultListener != null) {
                        onRTASRResultListener.onRTASRShortResult(str, mType);
                    }
                }

                if (onRTASRResultListener != null) {
                    onRTASRResultListener.onRTASRResult(stringBuffer.toString() + str, stringBuffer);
                }
            }
        } catch (Exception e) {
            return "";
        }
        return "";
    }

    private String getTranslateData(JSONObject messageObj) {
        String dst = messageObj.getString("dst");
        int type = Integer.parseInt(messageObj.getString("type"));
        boolean isEnd = messageObj.getBoolean("isEnd");
        if (dst == null) {
            dst = "";
        }
        if (type == 0) {
            translateStringBuffer.append(dst);
            if (onRTASRResultListener != null) {
                onRTASRResultListener.onRTASRTranslateShortResult(dst, type);
            }
            dst = "";
        } else {
            if (onRTASRResultListener != null) {
                onRTASRResultListener.onRTASRTranslateShortResult(dst, type);
            }
        }
        return dst;
    }


    public void clearData() {
        translateStringBuffer.setLength(0);
        stringBuffer.setLength(0);
    }


    public void onDestroy() {
        // 关闭 WebSocket 客户端
        if (client != null && client.isOpen()) {
            client.close();
            LogUtil.i(TAG + getCurrentTimeStr() + "\tWebSocket 客户端已关闭");
        }

        // 停止 WebSocket 客户端标志
        stopWebSocketClientTag = true;

        // 清除数据
        clearData();
        LogUtil.i(TAG + getCurrentTimeStr() + "\t数据已清除");
    }

    /***************************接口回调*****************************/
    private OnRTASRResultListener onRTASRResultListener;

    public void setOnRTASRResultListener(OnRTASRResultListener onRTASRResultListener) {
        this.onRTASRResultListener = onRTASRResultListener;
    }

    public interface OnRTASRResultListener {
         default void onRTASROffline(){};
        void onRTASRResult(String result, StringBuffer bufferResult);

        void onRTASRTranslateResult(String result, StringBuffer bufferResult);

        void onRTASRShortResult(String result, int type);  //type :0 为最终结果  1为过度结果

        void onRTASRTranslateShortResult(String result, int type);
    }

}